package com.ddxcloud.movie.service.impl;

import cn.hutool.core.date.*;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import cn.hutool.http.HttpRequest;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;

import com.ddxcloud.common.handler.BusinessException;
import com.ddxcloud.common.utils.RedisUtil;
import com.ddxcloud.common.utils.SignUtil;
import com.ddxcloud.movie.config.common.CommonProperties;
import com.ddxcloud.movie.config.movie.MovieConfig;
import com.ddxcloud.movie.data.dto.*;
import com.ddxcloud.movie.data.vo.*;
import com.ddxcloud.movie.data.entity.Channel;
import com.ddxcloud.movie.service.ChannelService;
import com.ddxcloud.movie.service.MoviesService;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class MoviesServiceImpl implements MoviesService {

    @Resource
    ChannelService channelService;

    @Resource
    CommonProperties commonProperties;

    @Resource
    MovieConfig movieConfig;

    @Resource
    RedisUtil redisUtil;

    @Override
    public JSONObject getAllCity() {
        Channel channel = channelService.getChannel();
        String url = movieConfig.getUrl() + "/open/common/getAllCity";
        String result = HttpRequest.get(url)
                .header("appId", channel.getChannelId())
                .timeout(20000)
                .execute().body();
        return JSONObject.parseObject(result);
    }

    @Override
    public Object getCity(String location) {
        // 调用高德根据经纬度获取城市
        String url = "https://restapi.amap.com/v3/geocode/regeo?location=" + location + "&key=" + commonProperties.getAmapKey() + "&output=json";
        String result = HttpRequest.get(url).execute().body();
        JSONObject body = JSONObject.parseObject(result);
        JSONObject regeocode = null;
        if (body != null) {
            regeocode = body.getJSONObject("regeocode");
        }
        JSONObject addressComponent = null;
        if (regeocode != null) {
            addressComponent = regeocode.getJSONObject("addressComponent");
        }
        Object city = null;
        if (addressComponent != null) {
            city = addressComponent.get("city");
        }
        if (city == null || city.equals("") || city.toString().equals("[]")) {
            if (addressComponent != null) {
                city = addressComponent.get("province");
            }
        }
        return city;
    }

    @Override
    public IPage<Object> getMovies(PageDTO dto) {
        Channel channel = channelService.getChannel();
        String url = movieConfig.getUrl() + "/open/movie/v1/getMovies";
        String result = HttpRequest.get(url)
                .header("appId", channel.getChannelId())
                .timeout(20000)
                .execute().body();
        JSONObject jsonObject = JSONObject.parseObject(result);
        return getResult(jsonObject.getJSONArray("data"), new ArrayList<>(), dto.getPageNum(), dto.getPageSize());
    }

    @Override
    public IPage<Object> getFutureMovies(PageDTO dto) {
        Channel channel = channelService.getChannel();
        String url = movieConfig.getUrl() + "/open/movie/v1/getFutureMovies";
        String result = HttpRequest.get(url)
                .header("appId", channel.getChannelId())
                .timeout(20000)
                .execute().body();
        JSONObject jsonObject = JSONObject.parseObject(result);
        return getResult(jsonObject.getJSONArray("data"), new ArrayList<>(), dto.getPageNum(), dto.getPageSize());
    }

    @Override
    public JSONObject getMovieInfo(String filmId) {
        Channel channel = channelService.getChannel();
        String url = movieConfig.getUrl() + "/open/movie/v1/getMovieInfo?filmId=" + filmId;
        String result = HttpRequest.get(url)
                .header("appId", channel.getChannelId())
                .timeout(20000)
                .execute().body();
        return JSONObject.parseObject(result);
    }

    @Override
    public List getCinemas(CinemasDTO dto) {
        Channel channel = channelService.getChannel();
        String url = movieConfig.getUrl() + "/open/movie/v1/getCinemas?cityId=" + dto.getCityId();
        String result = HttpRequest.get(url)
                .header("appId", channel.getChannelId())
                .timeout(20000)
                .execute().body();
        JSONObject jsonObject = JSONObject.parseObject(result);
        return JSONUtil.toList(jsonObject.getString("data"), CinemasVO.class);
    }

    @Override
    public JSONObject getCinemaInfo(String cityId, String cinemaId) {
        Channel channel = channelService.getChannel();
        String url = movieConfig.getUrl() + "/open/movie/v1/getCinemaInfo";
        Map<String, Object> params = new HashMap<>();
        params.put("cityId", cityId);
        params.put("cinemaId", cinemaId);
        String result = HttpRequest.get(url)
                .header("appId", channel.getChannelId())
                .form(params)
                .timeout(20000)
                .execute().body();
        return JSONObject.parseObject(result).getJSONObject("data");
    }

    @Override
    public List<ShowsVO> getShows(ShowsDTO dto) {
        Channel channel = channelService.getChannel();
        String url = movieConfig.getUrl() + "/open/movie/v1/getShows";
        Map<String, Object> params = new HashMap<>();
        params.put("cinemaId", dto.getCinemaId());
        params.put("searchFilmId", dto.getSearchFilmId());
        String result = HttpRequest.get(url)
                .header("appId", channel.getChannelId())
                .form(params)
                .timeout(20000)
                .execute().body();
        JSONObject jsonObject = JSONObject.parseObject(result);
        List<ShowsVO> list = JSONUtil.toList(jsonObject.getString("data"), ShowsVO.class);
        for (ShowsVO showsVO : list) {
            for (DatesVO datesVO : showsVO.getDates()) {
                for (ShowsDatesListVO listVO : datesVO.getList()) {
                    // 特惠出票结算价
                    listVO.setSettlementPrice(listVO.getSettlementPrice().add(channel.getChannelAmount()));
                    if (listVO.getShowPrice().compareTo(listVO.getSettlementPrice()) < 0) {
                        listVO.setShowPrice(listVO.getSettlementPrice());
                    }
                    this.getSpecialForShow(channel, listVO);
                }
            }
        }
        return list;
    }

    private void getSpecialForShow(Channel channel, ShowsDatesListVO listVO) {
        // 判断渠道ID为第五空间且是四月份期间
        if (channel.getChannelId().equals("1754424756389617665") && DateUtil.monthEnum(new Date()) == Month.APRIL) {
            boolean isTime = DateUtil.date().hour(true) == 10 || DateUtil.date().hour(true) == 11;
            // 每逢周三并且是上午10~11点之间设特价场次
            if (DateUtil.dayOfWeekEnum(new Date()) == Week.WEDNESDAY && isTime) {
                BigDecimal finalAmount = new BigDecimal("28.8");
                BigDecimal settlementPrice = listVO.getSettlementPrice().subtract(channel.getChannelAmount());
                // 特惠出票结算价位于30~38元之间，转为28.8元
                if (settlementPrice.compareTo(new BigDecimal(30)) >= 0 && settlementPrice.compareTo(new BigDecimal(38)) <= 0) {
                    listVO.setSettlementPrice(finalAmount);
                    listVO.setSpecialFlag(1); // 特惠场次
                }
            }
        }
        // 判断为中国移动且在四月份期间
        if (channel.getChannelId().equals("1751887856499974145") && DateUtil.monthEnum(new Date()) == Month.APRIL) {
            Date date = DateUtil.parse(listVO.getStartTime());
            // 2024-04-14号 &&《你想活出怎样的人生》
            if (DateUtil.formatDate(date).equals("2024-04-14") && listVO.getFilmId().equals("01200282024")) {
                log.info("FILM_ID: {} FILM_NAME: {} ", listVO.getFilmId(), listVO.getFilmName());
                String redisKey = "ACTIVITY_FILM_SHOW:" + listVO.getShowId();
                redisUtil.setEx(redisKey, JSONUtil.toJsonStr(listVO), 1, TimeUnit.DAYS);
            }
        }
    }

    @Override
    public SeatsVO getSeats(String showId, String cinemaId) {
        Channel channel = channelService.getChannel();
        String url = movieConfig.getUrl() + "/open/movie/v1/getSeats";
        Map<String, Object> params = new HashMap<>();
        params.put("showId", showId);
        params.put("cinemaId", cinemaId);
        String result = HttpRequest.get(url)
                .header("appId", channel.getChannelId())
                .form(params)
                .timeout(20000)
                .execute().body();
        JSONObject jsonObject = JSONObject.parseObject(result);
        jsonObject = jsonObject.getJSONObject("data");

        SeatsVO vo = new SeatsVO();
        vo.setColumns(jsonObject.getIntValue("columns"));
        vo.setRows(jsonObject.getIntValue("rows"));
        Map<String, Object> seats = jsonObject.getJSONObject("seats");
        Map<String, Object> seatsResult = new HashMap<>();
        for (Map.Entry<String, Object> entry : seats.entrySet()) {
            SeatsInfoVO info = JSONUtil.toBean(JSONUtil.toJsonStr(entry.getValue()), SeatsInfoVO.class);
            try {
                // 抬高猫眼市场价（虚假）
                info.setShowPrice(info.getShowPrice().add(channel.getChannelAmount()));
                // 抬高猫眼销售价（虚假）
                info.setSalePrice(info.getSalePrice().add(channel.getChannelAmount()));
                // 特惠出票结算价
                info.setSettlementPrice(info.getSettlementPrice().add(channel.getChannelAmount()));
                // 快捷出票结算价
                info.setSettlementQuickPrice(info.getSettlementQuickPrice().add(channel.getChannelAmount()));
                this.getSpecialForSeats(channel, info);
            } catch (Exception e) {
                log.error("seatId: {} errMsg: {}", info.getSeatId(), e.getMessage());
                info.setStatus(2); // 缺少结算金额的情况下标记该座位为不可售
            }
            seatsResult.put(entry.getKey(), info);
        }
        vo.setSeats(seatsResult);
        return vo;
    }

    private void getSpecialForSeats(Channel channel, SeatsInfoVO info) {
        // 判断渠道ID为第五空间且是四月份期间
        if (channel.getChannelId().equals("1754424756389617665") && DateUtil.monthEnum(new Date()) == Month.APRIL) {
            boolean isTime = DateUtil.date().hour(true) == 10 || DateUtil.date().hour(true) == 11;
            // 每逢周三并且是上午10~11点之间设特价场次
            if (DateUtil.dayOfWeekEnum(new Date()) == Week.WEDNESDAY && isTime) {
                BigDecimal finalAmount = new BigDecimal("28.8");
                BigDecimal settlementPrice = info.getSettlementPrice().subtract(channel.getChannelAmount());
                // 特惠出票结算价位于30~38元之间，转为28.8元
                if (settlementPrice.compareTo(new BigDecimal(30)) >= 0 && settlementPrice.compareTo(new BigDecimal(38)) <= 0) {
                    info.setSettlementPrice(finalAmount);
                }
            }
        }
    }

    @Override
    public JSONObject createOrder(AddOrderDTO dto, Integer specialFlag) {
        Channel channel = channelService.getChannel();
        String url = movieConfig.getUrl() + "/open/order/v1/addOrder";
        dto.setEncrypted(channel.getEncrypted());
        if (dto.getEncrypted()) {
            try {
                RSA rsa = new RSA(movieConfig.getSecret(), null);
                dto.setMobile(rsa.encryptBase64(dto.getMobile(), KeyType.PrivateKey));
            } catch (Exception e) {
                throw new BusinessException("订单加密失败，请检查参数是否有误");
            }
        }
        Map map = JSONUtil.toBean(JSONUtil.toJsonStr(dto), Map.class);
        map.put("sign", SignUtil.getSign(map, channel.getChannelSecret()));
        String result = HttpRequest.post(url)
                .header("appId", channel.getChannelId())
                .body(JSONUtil.toJsonStr(map))
                .timeout(20000)
                .execute().body();
        log.info("createOrder response params: {}", result);
        JSONObject jsonObject = JSONObject.parseObject(result);
        if (jsonObject.getInteger("code") == 0 && jsonObject.containsKey("data")) {
            try {
                AddOrderVO vo = JSONUtil.toBean(jsonObject.getString("data"), AddOrderVO.class);
                String[] seats = vo.getSeats().split(",");
                // 根据座位数 * 浮动金额
                BigDecimal amount = channel.getChannelAmount().multiply(BigDecimal.valueOf(seats.length));
                vo.setOrderAmount(vo.getOrderAmount().add(amount));
                this.getSpecialForOrder(vo, specialFlag, seats.length);
                jsonObject.put("data", JSONUtil.toJsonStr(vo));
            } catch (Exception e) {
                throw new BusinessException("提交订单失败，请重新下单");
            }
        } else {
            throw new BusinessException(jsonObject.getString("msg"));
        }
        return jsonObject;
    }

    private void getSpecialForOrder(AddOrderVO vo, Integer specialFlag, int seatsCount) {
        // 特惠场次并且为特惠购票的方式
        if (specialFlag == 1 && vo.getDeliverMode() == 0) {
            BigDecimal finalAmount = new BigDecimal("28.8");
            vo.setOrderAmount(finalAmount.multiply(new BigDecimal(seatsCount)));
        }
    }

    @Override
    public JSONObject confirmOrder(ConfirmOrderDTO dto, Channel channel) {
        String url = movieConfig.getUrl() + "/open/order/v1/confirmOrder";
        Map map = JSONUtil.toBean(JSONUtil.toJsonStr(dto), Map.class);
        map.put("sign", SignUtil.getSign(map, channel.getChannelSecret()));
        String result = HttpRequest.post(url)
                .header("appId", channel.getChannelId())
                .body(JSONUtil.toJsonStr(map))
                .timeout(20000)
                .execute().body();
        log.info("confirmOrder response params: {}", result);
        return JSONObject.parseObject(result);
    }

    /**
     * 分页方法
     * @param data
     * @param listsort
     * @param pageNum
     * @param pageSize
     * @return
     */
    @NotNull
    private IPage<Object> getResult(List data, List listsort, Integer pageNum, Integer pageSize) {
        for(int i=(pageNum>1?pageSize*(pageNum-1):0);i<(pageNum*pageSize>data.size()?(pageNum*pageSize)-((pageNum*pageSize)-data.size()):pageNum*pageSize);i++){
            listsort.add(data.get(i));
        }
        IPage<Object> page = new Page<>();
        page.setCurrent(pageNum);
        page.setSize(pageSize);
        page.setTotal(data.size());
        page.setRecords(listsort);
        page.setPages((data.size() / pageSize) + (data.size() % pageSize > 0 ? 1 : 0));
        return page;
    }
}
